/*
 * Project it.battlehorse.rcp.ioc
 * Created on Apr 8, 2006
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * A copy of the LGPL is available also at
 * http://www.gnu.org/copyleft/lesser.html
 * 
 */
package it.battlehorse.ioc;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.CheckClassAdapter;

/**
 * The java agent which performs the bytecode manipulation. The agent can work in debug mode, which
 * activates a more detailed logging (to System.out) and a validation layer to verify bytecode modifications.
 * To enable the debug mode, set the <code>ioctransformer.debug</code> system property to <code>true</code>
 * at JVM startup time.
 * 
 * @author battlehorse
 * @since Apr 8, 2006
 */
public class IOCTransformer implements ClassFileTransformer {
	
	private static boolean DEBUG ;
	
	static {
		String slDebug = System.getProperty("ioctransformer.debug","false");
		DEBUG = slDebug.equalsIgnoreCase("true");		
	}

	/* (non-Javadoc)
	 * @see java.lang.instrument.ClassFileTransformer#transform(java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[])
	 */
	public byte[] transform(ClassLoader loader, String className,
			Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
			byte[] classfileBuffer) throws IllegalClassFormatException {
		
		
		if (DEBUG)
			System.out.println("IOCT: Parsing class " + className);
		
		ClassReader creader = null;
		try {
			/* for some misterious reason on Mac Os X the JVM will freeze 
			 * if we don't wrap the byte array into an InputStream ...
			 * It seems to be in someway related with the -XstartOnFirstThread option
			 * which must be used on Mac Os X to be able to run SWT applications ...
			 * And it appears to work only on JVM 6.0 ...
			 */
			creader = new ClassReader(new ByteArrayInputStream(classfileBuffer));
			
		}
		catch(IOException ioe) {
			ioe.printStackTrace();	
		}
		
		//creader = new ClassReader(classfileBuffer);
		
				
		// Parse the class file
		ConstructorVisitor cv = new ConstructorVisitor();
		ClassAnnotationVisitor cav = new ClassAnnotationVisitor(cv);		
		creader.accept(cav, ClassReader.SKIP_DEBUG);
		
		if (cv.getConstructors().size() > 0) {
			if (DEBUG)
				System.out.println("IOCT: Enhancing " + className);
			// Generate the enhanced-constructor class
			ClassWriter cw = new ClassWriter(0);
			
			ClassVisitor cvisitor = null;
			if (DEBUG) {
				CheckClassAdapter chk = new CheckClassAdapter(cw);
				cvisitor = chk;
				//TraceClassVisitor tv = new TraceClassVisitor(cw, new PrintWriter(System.out));
				//cvisitor = tv;
			}
			else {
				cvisitor = cw;
			}			
			ClassConstructorWriter writer = new ClassConstructorWriter(cv.getConstructors(),cvisitor);		
			
			//ClassConstructorWriter writer = new ClassConstructorWriter(cv.getConstructors(),cw);
			creader.accept(writer, ClassReader.SKIP_DEBUG);
			
	        return cw.toByteArray();
		}
		else
			return null;
	}
	
	public static void premain(String agentArgs, Instrumentation inst) {
		inst.addTransformer(new IOCTransformer());
	}

}
